
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.XPath;

using Microsoft.Ddue.Tools;
using System.Windows.Forms;

using Darilek.SandcastleExtensions.Components.Configuration;


namespace Darilek.SandcastleExtensions.Components
{
        /// <summary>
        /// Filter out keywords from HTML files when there are too many of the same keyword present in the corpus.
        /// </summary>
        /// <remarks>
        /// The max number of entries per keyword defaults to 100, 
        /// but can be overridden using the component config file.
        /// <para/>
        /// This component must be started at the end of build process
        /// (before last step - saving output into file).
        /// </remarks>
        /// <example>
        /// Configuration file should look like this:
        /// <code lang="xml">
        /// &lt;MaxKeywords>
        /// 123
        /// &lt;/MaxKeywords>
        /// </code>
        /// </example>
        public class KeywordsFilterComponent : BuildComponent
        {

            int _maxKeywords = 100;
            Dictionary<string, List<string>> _tracker = new Dictionary<string, List<string>>();

            #region Constructor

			/// <summary>
			/// Component constructor
			/// </summary>
			/// <param name="assembler"></param>
			/// <param name="configuration"></param>
			public KeywordsFilterComponent(BuildAssembler assembler, XPathNavigator configuration)
                : base(assembler, configuration)
            {
                base.WriteMessage(MessageLevel.Info, "Keywords Filter Component");

                // read configuration
                XPathNavigator node = configuration.SelectSingleNode("maxKeywordsCount");
                if (node != null)
                {
                    _maxKeywords = node.ValueAsInt;
                }
                _tracker.Clear();

                base.WriteMessage(MessageLevel.Info, String.Format("Removing keywords with more than {0} instances", _maxKeywords));
            }

            #endregion

            /// <summary>
            /// Count keywords per document. Keep track of which documents contain which keywords
            /// </summary>
            /// <param name="document">The XML document with which to work.</param>
            /// <param name="key">The key (member name) of the item being
            /// documented.</param>
            public override void Apply(XmlDocument document, string key)
            {
                XmlNamespaceManager mgr = new XmlNamespaceManager(document.NameTable);
                mgr.AddNamespace("MSHelp", "http://msdn.microsoft.com/mshelp");
                mgr.AddNamespace("msxsl", "urn:schemas-microsoft-com:xslt");

                XmlNode headNode = document.SelectSingleNode("/html/head", mgr);
                if (headNode == null)
                {
                    base.WriteMessage(MessageLevel.Warn, String.Format("<head> element no found in {0}", key));
                    base.WriteMessage(MessageLevel.Warn, document.OuterXml);
                    return;
                }

                XmlNode xmlNode = document.SelectSingleNode("/html/head/xml", mgr);
                if (xmlNode == null)
                {
                    base.WriteMessage(MessageLevel.Warn, String.Format("<xml> element no found in {0}", key));
                    base.WriteMessage(MessageLevel.Warn, document.OuterXml);
                    return;
                }

                // <MSHelp:Keyword Index="K" xmlns:MSHelp="http://msdn.microsoft.com/mshelp">
                //    <includeAttribute name="Term" item="propertyIndexEntry">
                //       <parameter>Competitor</parameter>
                //    </includeAttribute>
                // </MSHelp:Keyword>

                XmlNodeList xmlNodes = xmlNode.SelectNodes("MSHelp:Keyword/includeAttribute/parameter", mgr);
                if (xmlNodes == null || xmlNodes.Count == 0)
                {
                    base.WriteMessage(MessageLevel.Warn, String.Format("Unable to find Keyword nodes in {0}", key));
                    return;
                }

                // xmlNodes = parameter
                foreach (XmlNode node in xmlNodes)
                {
                    string keyword = node.InnerText;
                    string filename = key;

                    if (!_tracker.ContainsKey(keyword))
                        _tracker.Add(keyword, new List<string>());
                    _tracker[keyword].Add(filename);
                    if (_tracker[keyword].Count > _maxKeywords)
                    {
                        base.WriteMessage(MessageLevel.Warn, String.Format("Keyword {0} exceeds count in {1}", keyword, key));
                        XmlNode keyWordNode = node.ParentNode.ParentNode;
                        keyWordNode.ParentNode.RemoveChild(keyWordNode);
                    }
                }

            }


            #region Component configuration

            /// <summary>
            /// Configuration interface for Sandcastle Help File Builder (http://www/codeplex.com/shfb) tool
            /// </summary>
            /// <param name="currentConfig">Current component configuration</param>
            /// <returns>Modified component configuration</returns>
            public static string ConfigureComponent(string currentConfig)
            {
				using (KeywordsFilterConfigDlg dlg = new KeywordsFilterConfigDlg(currentConfig))
				{
					if (dlg.ShowDialog() == DialogResult.OK)
					{
						return dlg.Configuration;
					}
				}

				return currentConfig;
            }

            #endregion
        }
}
